#!/usr/bin/python2.4
#
# Copyright 2009 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Primary handler for returning post(s)."""

# Cruise Control
import base_handler

# Python
import calendar
import datetime
import logging


def GetQueryDateRangeFromPathList(path_list):
  """Given a path list (i.e., the user path split by '/') returns the daterange.

  Args:
    path_list: a list containing the path elements

  Returns:
    start_date: date object or False if bad path supplied
    end_date: date object or False if bad path supplied
  """
  path_length = len(path_list)
  start_date = False
  end_date = False
  try:
    # Requested posts for a year
    if path_length == 1:
      start_date = datetime.date(int(path_list[0]), 1, 1)
    # Requested posts for a month
    elif path_length == 2:
      start_date = datetime.date(int(path_list[0]), int(path_list[1]), 1)
    # Requested posts for a day
    else:
      start_date = datetime.date(int(path_list[0]), int(path_list[1]),
                                 int(path_list[2]))
  except ValueError:
    logging.error("""base_handler.py - Some param out of range.
                  GetQueryDateRangeFromPathList: %s""" % str(path_list))

  # Bad path was requested
  if not start_date:
    return start_date, end_date

  if path_length == 1:
    leap_day = 0
    if calendar.isleap(start_date.year):
      leap_day = 1
    end_date = start_date + datetime.timedelta(days=(365 + leap_day))

  elif path_length == 2:
    one_month = calendar.monthrange(start_date.year, start_date.month)
    end_date = start_date + datetime.timedelta(days=one_month[1])

  else:
    end_date = start_date + datetime.timedelta(days=1)

  return start_date, end_date

def UserRequestedDate(path_list):
  """Given a path list returns a date object.

  This function expects all three elements of a date (year, month, day). If it
  does not get those elements it returns None.

  Args:
    path_list: a list containing the path elements

  Returns:
    user_date: date object if successful or None if not
  """
  user_date = None
  try:
    user_date = datetime.date(int(path_list[0]), int(path_list[1]),
                              int(path_list[2]))
  except ValueError:
    logging.error('posts.py - Some param out of range. UserRequestedDate: %s' %
                  str(path_list))
  except TypeError:
    logging.error('posts.py - Missing/invalid param. UserRequestedDate: %s' %
                  str(path_list))
  return user_date


class Date(base_handler.BaseHandler):
  """Handles returning posts for a requested date range.

  For example, a user might want all the posts made for a specific time frame:
    - year = http://myapp.com/2009/
    - month = http://myapp.com/2009/02/
    - day = http://myapp.com/2009/02/02
  """
  def Call(self):
    # Lets the template know what kind of data is going to be returned
    self.html_vars['date_query'] = True

    start_date, end_date = GetQueryDateRangeFromPathList(self.path_list)

    if start_date and end_date:
      posts = self.db.GetPostsForDates(start_date, end_date)
      if not posts.count():
        self.html_vars['no_posts_found'] = True
      else:
        self.html_vars['posts'] = posts
    else:
      self.html_vars['bad_date_request'] = True
      self.http_status_code = 404

    self.RenderPage('posts.html')


class Post(base_handler.BaseHandler):
  """Handles looking up a specific post for the user.

  If the user has the post path correct, but the date incorrect, we suggest the
  post to the user.
  """
  def FilterComments(self, all_comments):
    """Given a list of comments filters unreviewed/rejected based on the user.

    Args:
      all_comments: gql query object containing the comments for a post.

    Returns:
      a list of displayable comments.
    """
    comments = []
    for comment in all_comments:
      is_approved = (comment.status == 'approved')
      is_user_owned = (self.user and comment.author.key() == self.user.key())
      # Only admins will ever see unreviewed comments
      needs_review = (self.is_admin and comment.status == 'unreviewed')

      if is_approved or is_user_owned or needs_review:
        comments.append(comment)
    return comments

  def Call(self):
    """Handles returning the desired post and adding comments for a post."""
    # Add comment if present
    if self.request_method == 'POST' and self.params.get('new-comment'):
      author = self.params.get('author')
      if author:
        params = {'user': author}
        for param in self.params:
          params[param] = self.params[param]
        user_entity, warnings = self.db.SetUser(params)

        if not warnings:
          self.user = user_entity
          self.html_vars['user'] = self.user
          comment_entity, warnings = self.db.SetComment(params)
        else:
          self.html_vars['user'] = {'url': user_entity['url'],
                                    'nickname': user_entity['nickname'],
                                    'key': self.user.key()}
          self.html_vars['comment'] = {'body': self.params['body']}

        if warnings:
          self.html_vars['warnings'] = warnings

    # Try to grab the requested post(s)

    # Grab the date from the path
    user_date = UserRequestedDate(self.path_list)

    # Thanks to app.py we know that we have the following path list structure:
    # [2009, 02, 02, post-title]
    post_path = self.path_list[-1]  # Grab the path

    # Query for the desired post
    posts = self.db.GetPostsByPath(post_path)

    # Check to see if we have a post that matches the date
    matches = []
    for post in posts:
      if post.is_published:
        post_date = post.publish_date.date()
        if post_date == user_date:
          matches.append(post)

    # Yay! We have the right post!
    if len(matches) == 1:
      self.html_vars['post'] = matches[0]
      self.html_vars['page_title'] = matches[0].title
      self.SetMetaInfo(matches[0])

      all_comments = self.db.GetCommentsForPost(matches[0])
      comments = self.FilterComments(all_comments)
      self.html_vars['post_comments'] = comments

    else:
      # We shouldn't ever have more than one post match a requested post path,
      # but just in case we do, don't fail the user. Return what we can.
      self.http_status_code = 404
      possible_posts = None
      if matches:
        possible_posts = matches
      elif posts.count() and not matches:
        # Here we found a post with the appropriate path, but the date did not
        # match.
        possible_posts = posts
        self.html_vars['are_suggested_posts'] = True
      else:
        self.html_vars['no_posts_found'] = True

      self.html_vars['possible_posts'] = possible_posts
    self.RenderPage('posts.html')
